package gui;

import java.awt.Point;
import java.text.DecimalFormat;
import java.util.ArrayList;

/**
 * @author Ramy Refky 
 * */
public class Wire {
	
	MainGui mg;
	ArrayList<Point> al;
	ArrayList<WireSegment> wireSegments;
	Point startPoint;
	Point endPoint;
	
	ComponentPin firstPin;
	ComponentPin secondPin;
	
	boolean grouped;
	
	/**
	 * Constructor initializes a wire connecting two ComponentPins. The wire is in it's default position.
	 * @param MainGui, FirstPin, SecondPin
	 * */
	public Wire(MainGui mg,ComponentPin firstPin,ComponentPin secondPin){
		
		this.mg=mg;
		this.firstPin=firstPin;
		this.secondPin=secondPin;
		
		al= new ArrayList<Point>();
		wireSegments= new ArrayList<WireSegment>();
		startPoint =new Point();
		endPoint =new Point();
		grouped=false;
		
		connect(firstPin,secondPin,0,0,0,0);
	}
	/**
	 * Constructor initializes a wire connecting two ComponentPins. The wire's position is given offset values.
	 * @param MainGui, FirstPin, SecondPin, offsetTypeA, offsetTypeB, offsetTypeC, offsetTypeD
	 * */
	public Wire(MainGui mg,ComponentPin firstPin,ComponentPin secondPin,double offsetTypeA,
			double offsetTypeB,double offsetTypeC,double offsetTypeD){
		
		this.mg=mg;
		this.firstPin=firstPin;
		this.secondPin=secondPin;
		
		al= new ArrayList<Point>();
		wireSegments= new ArrayList<WireSegment>();
		startPoint =new Point();
		endPoint =new Point();
		grouped=false;
		
		connect(firstPin,secondPin,offsetTypeA,offsetTypeB,offsetTypeC,offsetTypeD);
	}
	/**
	 * Constructor initializes a wire connecting two ComponentPins. The wire's position is given offset values.
	 * And the wire is placed on a given WorkArea.
	 * @param WorkArea, FirstPin, SecondPin, offsetTypeA, offsetTypeB, offsetTypeC, offsetTypeD
	 * */
	public Wire(WorkArea wa,ComponentPin firstPin,ComponentPin secondPin,double offsetTypeA,
			double offsetTypeB,double offsetTypeC,double offsetTypeD){
		
		this.firstPin=firstPin;
		this.secondPin=secondPin;
		
		al= new ArrayList<Point>();
		wireSegments= new ArrayList<WireSegment>();
		startPoint =new Point();
		endPoint =new Point();
		grouped=false;
		
		connect(wa,firstPin,secondPin,offsetTypeA,offsetTypeB,offsetTypeC,offsetTypeD);
	}
	/**
	 * Returns the wire's points in an ArrayList.
	 * @return ArrayList
	 * */
	public ArrayList<Point> getPointsArray(){
		return al;
	}
	/**
	 * Checks if the wire is connected to a given ComponentPin.
	 * @return boolean
	 * */
	public boolean isConnectedTo(ComponentPin pin){
		boolean connected=false;
		if(firstPin.equals(pin) || secondPin.equals(pin)){
			connected=true;
		}
		return connected;
	}
	/**
	 * Returns the Wire's WireSegments in an ArrayList.
	 * @return ArrayList
	 * */
	public ArrayList<WireSegment> getWireSegments(){
		return wireSegments;
	}
	/**
	 * Returns the WireSegment ending at a given point.
	 * @return WireSegment
	 * */
	public WireSegment getWireSegmentEndingAt(Point endPoint){
		for(int i=0;i<wireSegments.size();i++){
			if(endPoint.equals(wireSegments.get(i).getEndPoint())){
				
				return wireSegments.get(i);
			}
		}
		return null;
	}
	/**
	 * Returns the WireSegment starting at a given point.
	 * @return WireSegment
	 * */
	public WireSegment getWireSegmentStartingAt(Point startPoint){
		for(int i=0;i<wireSegments.size();i++){
		
			if(startPoint.equals(wireSegments.get(i).getStartPoint())){
				return wireSegments.get(i);
			}
		}
		return null;
	}
	/**
	 * Connects the Wire between two given ComponentPins, and offset values are zero by default.
	 * */
	public void connect(ComponentPin firstPin, ComponentPin secondPin,double offsetTypeA,
			double offsetTypeB,double offsetTypeC,double offsetTypeD){
		this.firstPin=firstPin;
		this.secondPin=secondPin;
		al.clear();
		wireSegments.clear();
		mg.getWorkArea().removeWire(this);
		//inputPin to outputPin
		
		if(!firstPin.getType() && secondPin.getType()){
			Point ptA=firstPin.getStartPoint();
			Point ptB=secondPin.getEndPoint();
			//outputPin.x<inputPin.x
			if(ptA.x>=ptB.x){
				
				int midX;
				if(offsetTypeA==0){
					midX= ((ptA.x+ ptB.x)/2);
				}else{
					midX= (int) ((ptA.x-ptB.x+0.0)*offsetTypeA)+ptB.x;
				}
				Point p1= new Point(ptA.x,ptA.y);
				Point p2= new Point(midX,ptA.y);
				Point p3= new Point(midX,ptB.y);
				Point p4= new Point(ptB.x,ptB.y);
				al.add(p4);
				al.add(p3);
				al.add(p2);
				al.add(p1);	
				
				startPoint=new Point(p4);
				endPoint=new Point(p1);
				
				wireSegments.add(0,new WireSegment(mg,this,new Point(p4),new Point(p3)));
				mg.getWorkArea().addWireSegment(wireSegments.get(0));
				wireSegments.add(1,new WireSegment(mg,this,new Point(p3),new Point(p2)));
				mg.getWorkArea().addWireSegment(wireSegments.get(1));
				wireSegments.add(2,new WireSegment(mg,this,new Point(p2),new Point(p1)));
				mg.getWorkArea().addWireSegment(wireSegments.get(2));
			}
			else{
				//outputPin.x>inputPin.x outputPin.y>inputPin.y 
				if(ptA.x<ptB.x ){
					//int midY= (ptA.y+ptB.y)/2;	
					
					int midY;
					if(offsetTypeD==0){
						midY= ((ptA.y+ptB.y)/2);
					}else{
						midY= (int) ((ptA.y-ptB.y+0.0)*-offsetTypeD)+ptB.y;
					}
					Point p1= new Point(ptA.x,ptA.y);
					
					Point p2= new Point(ptA.x-10,ptA.y);
					Point p3= new Point(ptA.x-10,midY);
					Point p4= new Point(ptB.x+10,midY);
					Point p5= new Point(ptB.x+10,ptB.y);
					Point p6= new Point(ptB.x,ptB.y);
					
					if(offsetTypeB>0){
						int segment1Width= (int) (((p6.x+0.0)-(p1.x+0.0))*offsetTypeB);
						System.out.println(segment1Width+"");
						p5.x=ptB.x+segment1Width;
						p4.x=ptB.x+segment1Width;
					}
					if(offsetTypeC>0){
						int segment2Width= (int) (((p6.x+0.0)-(p1.x+0.0))*offsetTypeC);
						System.out.println(segment2Width+"");
						p3.x=ptA.x-segment2Width;
						p2.x=ptA.x-segment2Width;
					}
					
					al.add(p6);
					al.add(p5);
					al.add(p4);
					al.add(p3);	
					al.add(p2);
					al.add(p1);
					

					startPoint=new Point(p6);
					endPoint=new Point(p1);
					
					wireSegments.add(0,new WireSegment(mg,this,new Point(p6),new Point(p5)));
					mg.getWorkArea().addWireSegment(wireSegments.get(0));
					wireSegments.add(1,new WireSegment(mg,this,new Point(p5),new Point(p4)));
					mg.getWorkArea().addWireSegment(wireSegments.get(1));
					
					p4.x+=4;
					p3.x-=4;
					wireSegments.add(2,new WireSegment(mg,this,new Point(p4),new Point(p3)));
					mg.getWorkArea().addWireSegment(wireSegments.get(2));
					p3.x+=4;
					wireSegments.add(3,new WireSegment(mg,this,new Point(p3),new Point(p2)));
					mg.getWorkArea().addWireSegment(wireSegments.get(3));
					wireSegments.add(4,new WireSegment(mg,this,new Point(p2),new Point(p1)));	
					mg.getWorkArea().addWireSegment(wireSegments.get(4));
				}
			}
		}
	}
	/**
	 * Connects the Wire between two given ComponentPins on a given WorkArea,
	 * and offset values are zero by default.
	 * */
	public void connect(WorkArea wa,ComponentPin firstPin, ComponentPin secondPin,double offsetTypeA,
			double offsetTypeB,double offsetTypeC,double offsetTypeD){
		this.firstPin=firstPin;
		this.secondPin=secondPin;
		al.clear();
		wireSegments.clear();
		wa.removeWire(this);
		//inputPin to outputPin
		
		if(!firstPin.getType() && secondPin.getType()){
			Point ptA=firstPin.getStartPoint();
			Point ptB=secondPin.getEndPoint();
			//outputPin.x<inputPin.x
			if(ptA.x>=ptB.x){
				
				int midX;
				if(offsetTypeA==0){
					midX= ((ptA.x+ ptB.x)/2);
				}else{
					midX= (int) ((ptA.x-ptB.x+0.0)*offsetTypeA)+ptB.x;
				}
				Point p1= new Point(ptA.x,ptA.y);
				Point p2= new Point(midX,ptA.y);
				Point p3= new Point(midX,ptB.y);
				Point p4= new Point(ptB.x,ptB.y);
				al.add(p4);
				al.add(p3);
				al.add(p2);
				al.add(p1);	
				
				startPoint=new Point(p4);
				endPoint=new Point(p1);
				
				wireSegments.add(0,new WireSegment(mg,this,new Point(p4),new Point(p3)));
				wa.addWireSegment(wireSegments.get(0));
				wireSegments.add(1,new WireSegment(mg,this,new Point(p3),new Point(p2)));
				wa.addWireSegment(wireSegments.get(1));
				wireSegments.add(2,new WireSegment(mg,this,new Point(p2),new Point(p1)));
				wa.addWireSegment(wireSegments.get(2));
			}
			else{
				//outputPin.x>inputPin.x outputPin.y>inputPin.y 
				if(ptA.x<ptB.x ){
					//int midY= (ptA.y+ptB.y)/2;	
					
					int midY;
					if(offsetTypeD==0){
						midY= ((ptA.y+ptB.y)/2);
					}else{
						midY= (int) ((ptA.y-ptB.y+0.0)*-offsetTypeD)+ptB.y;
					}
					Point p1= new Point(ptA.x,ptA.y);
					
					Point p2= new Point(ptA.x-10,ptA.y);
					Point p3= new Point(ptA.x-10,midY);
					Point p4= new Point(ptB.x+10,midY);
					Point p5= new Point(ptB.x+10,ptB.y);
					Point p6= new Point(ptB.x,ptB.y);
					
					if(offsetTypeB>0){
						int segment1Width= (int) (((p6.x+0.0)-(p1.x+0.0))*offsetTypeB);
						System.out.println(segment1Width+"");
						p5.x=ptB.x+segment1Width;
						p4.x=ptB.x+segment1Width;
					}
					if(offsetTypeC>0){
						int segment2Width= (int) (((p6.x+0.0)-(p1.x+0.0))*offsetTypeC);
						System.out.println(segment2Width+"");
						p3.x=ptA.x-segment2Width;
						p2.x=ptA.x-segment2Width;
					}
					
					al.add(p6);
					al.add(p5);
					al.add(p4);
					al.add(p3);	
					al.add(p2);
					al.add(p1);
					

					startPoint=new Point(p6);
					endPoint=new Point(p1);
					
					wireSegments.add(0,new WireSegment(mg,this,new Point(p6),new Point(p5)));
					wa.addWireSegment(wireSegments.get(0));
					wireSegments.add(1,new WireSegment(mg,this,new Point(p5),new Point(p4)));
					wa.addWireSegment(wireSegments.get(1));
					
					p4.x+=4;
					p3.x-=4;
					wireSegments.add(2,new WireSegment(mg,this,new Point(p4),new Point(p3)));
					wa.addWireSegment(wireSegments.get(2));
					p3.x+=4;
					wireSegments.add(3,new WireSegment(mg,this,new Point(p3),new Point(p2)));
					wa.addWireSegment(wireSegments.get(3));
					wireSegments.add(4,new WireSegment(mg,this,new Point(p2),new Point(p1)));	
					wa.addWireSegment(wireSegments.get(4));
				}
			}
		}
	}
	/**
	 * Returns the wire to it's default position.
	 * */
	public void refreshWire(){
		connect(firstPin,secondPin,0,0,0,0);
	}
	/**
	 * Adjust's the wire's position while maintaining its modification. Then an ArrayList of the wire's
	 * offsets is returned.
	 * @return ArrayList
	 * */
	public ArrayList<Double> maintainWire(){
		
		if(getWireSegments().size()==3){
			Point pt1= new Point(getWireSegments().get(0).getStartPoint());
			Point pt2= new Point(getWireSegments().get(1).getStartPoint());
			Point pt4= new Point(getWireSegments().get(2).getEndPoint());
			DecimalFormat df= new DecimalFormat("#.##");
			Double offsetRatio = (pt2.x-pt1.x+0.0)/(pt4.x-pt1.x+0.0);		
			offsetRatio= Double.valueOf(df.format(offsetRatio));
			connect(firstPin,secondPin,offsetRatio,0,0,0);
			ArrayList<Double> result = new ArrayList<Double>();
			result.add(offsetRatio);
			return result;
		}
		else{
			if(getWireSegments().size()==5){
				
				Point pt1= new Point(getWireSegments().get(0).getStartPoint());
				Point pt2= new Point(getWireSegments().get(1).getStartPoint());
				
				Point pt3= new Point(getWireSegments().get(2).getStartPoint());
				Point pt5= new Point(getWireSegments().get(4).getStartPoint());
				Point pt6= new Point(getWireSegments().get(4).getEndPoint());
				
				DecimalFormat df= new DecimalFormat("#.##");
				
				//ratio between wire 1 and pin distances
				Double offsetRatio1 = (pt2.x-pt1.x+0.0)/(pt1.x-pt6.x+0.0);
				offsetRatio1= Double.valueOf(df.format(offsetRatio1));
				
				//ratio between wire 5 and pin distances
				Double offsetRatio2 = (pt6.x-pt5.x+0.0)/(pt1.x-pt6.x+0.0);	
				offsetRatio2= Double.valueOf(df.format(offsetRatio2));
				
				//ratio between wire 2 and pin heights
				Double offsetRatio3 = (pt3.y-pt2.y+0.0)/(pt1.y-pt6.y+0.0);	
				offsetRatio3= Double.valueOf(df.format(offsetRatio3));
				
				connect(firstPin,secondPin,0,offsetRatio1,offsetRatio2,offsetRatio3);
				
				ArrayList<Double> result = new ArrayList<Double>();
				result.add(offsetRatio1);
				result.add(offsetRatio2);
				result.add(offsetRatio3);
				return result;
			}	
			return new ArrayList<Double>();
		}
	}
	/**
	 * Returns the Wire's starting point.
	 * @return Point
	 * */
	public Point getStartPoint(){
		return startPoint;
	}
	/**
	 * Returns the Wire's ending point.
	 * @return Point
	 * */
	public Point getEndPoint(){
		return endPoint;
	}
	/**
	 * Returns the Wire's first ComponentPin.
	 * @return ComponentPin
	 * */
	public ComponentPin getFirstPin(){
		return firstPin;
	}
	/**
	 * Returns the Wire's second ComponentPin.
	 * @return ComponentPin
	 * */
	public ComponentPin getSecondPin(){
		return secondPin;
	}
	/**
	 * Checks if both components connected by the wire are selected.
	 * @return boolean
	 * */
	public boolean areBothEndsSelected(){
		if(firstPin.getParentComponent().isSelected() && secondPin.getParentComponent().isSelected())
			return true;
		return false;
	}
	/**
	 * Checks if only one component connected by the wire is selected.
	 * @return boolean
	 * */
	public boolean isOnlyOneEndSelected(){
		if(firstPin.getParentComponent().isSelected() && !secondPin.getParentComponent().isSelected())
			return true;
		if(!firstPin.getParentComponent().isSelected() && secondPin.getParentComponent().isSelected())
			return true;
		return false;
	}
	/**
	 * Checks if the wire is grouped with other wires.
	 * @return boolean
	 * */
	public boolean isGrouped(){
		return grouped;
	}
	/**
	 * Sets the wire to be grouped with other grouped wires.
	 * */
	public void setAsGrouped(){
		grouped=true;
	}
}
